package blazingbubble.gui;
import java.io.File;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.net.UnknownHostException;

import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

import org.apache.log4j.Logger;

import spark.network.NetworkConnection;
import spark.network.Sender;
import spark.network.ServerConnection;
import spark.network.SessionFactory;
import spark.utility.Clock;
import spark.utility.FrameBreak;

import blazingbubble.config.Config;
import blazingbubble.config.ConfigReader;
import blazingbubble.config.exception.ConfigReaderException;
import blazingbubble.config.exception.ConfigValidationException;
import blazingbubble.exception.StartupException;
import blazingbubble.game.Game;
import blazingbubble.game.ILobbyGameEventListener;
import blazingbubble.game.command.GameCommandFactory;
import blazingbubble.game.command.net.NetGameCommandFactory;
import blazingbubble.graphics.GameWindow;
import blazingbubble.gui.panels.BrowseGamesPanel;
import blazingbubble.gui.panels.GameLobbyPanel;
import blazingbubble.gui.panels.SettingsPanel;
import blazingbubble.lobby.Lobby;
import blazingbubble.lobby.command.net.NetLobbyCommandFactory;
import blazingbubble.network.game.GameStrategyFactory;
import blazingbubble.network.game.NetGame;
import blazingbubble.network.game.strategy.IJoinGameResponseResultListener;
import blazingbubble.network.game.strategy.IPlayerTeamJoinListener;
import blazingbubble.network.game.strategy.IStartGameListener;
import blazingbubble.network.lobby.LobbyStrategyFactory;

import moltencore.game.Settings;
import moltencore.game.loader.GameCreator;
import moltencore.game.loader.exception.FieldCreationException;
import moltencore.game.loader.exception.GameCreationException;
import moltencore.network.game.GameDemon;
import moltencore.network.game.GameLobby;
import moltencore.network.serverlobby.exception.LobbyCreationException;
import napkin.NapkinLookAndFeel;



public class BlazingBubble implements ILobbyGameEventListener, IStartGameListener{
	private static Logger logger = Logger.getLogger(BlazingBubble.class);

	private static final String USAGE =
			"<configFile> <resourcesPath>\n\n" +
			"-configFile: path to the file specifying the server config (usually server.ini).\n" +
			"-resourcesPath: path to the resources folder";
	private static final String NO_RESOURCES =
			"Resources folder does not exist";
	
	private static final String UNSUPPORTED_LNF =
			"Unsupported look&feel";

	private static final int ARGS_INDEX_CONFIG = 0;
	private static final int ARGS_INDEX_RESOURCES = 1;
	private static final int ARGS_COUNT = 2;
	
	private MainWindow mainWindow;
	private Game game = new Game();

	private Lobby lobby = new Lobby();

	private NetGame netGame = new NetGame();
	private GameCommandFactory gameCommandFactory;
	
	private GameDemon serverGameDemon = null;
	private moltencore.game.Game serverGame = null;

	private Config config;
	
	private BlazingBubble(){
		super();
	}
	
	public static void main(String [] args){
		try {
			UIManager.setLookAndFeel(new NapkinLookAndFeel());
		}
		catch (UnsupportedLookAndFeelException e) {
			logger.fatal(UNSUPPORTED_LNF);
		}
		
		if(args.length != ARGS_COUNT) {
			logger.fatal(USAGE);
			return;
		}
		File resourcesFolder = new File(args[ARGS_INDEX_RESOURCES]);
		if(!resourcesFolder.exists() || !resourcesFolder.isDirectory()) {
			logger.fatal(NO_RESOURCES + "(" + resourcesFolder.getAbsolutePath() + ")");
		}
		Config.setResourcePath(resourcesFolder.getPath());
		Config config = null;
		try {
			config = new ConfigReader().readConfig(args[ARGS_INDEX_CONFIG], Config.getMultiplayerFieldConfig());
			config.validate();
		}
		catch(ConfigReaderException e) {
			logger.fatal(e);
			return;
		}
		catch(ConfigValidationException e) {
			logger.fatal(e);
			return;
		}
		
		BlazingBubble client = new BlazingBubble();

		try {
			client.startup(config);
			client.execute();
		}
		catch(Throwable e) {
			e.printStackTrace();
			logger.fatal(e);
		}
		finally {
			client.shutdown();
		}
	}
	
	public void startup(Config config) throws StartupException {
		this.config = config;

		LobbyStrategyFactory lobbyStrategyFactory = new LobbyStrategyFactory();
		lobby.startup(config, lobbyStrategyFactory);
		NetLobbyCommandFactory lobbyNetCommandFactory = new NetLobbyCommandFactory();
		lobbyNetCommandFactory.startup(lobby.getSender(), lobby.getServerConnection(), lobby.getQueryPort());

		lobby.setPreLoginResponseListener(lobbyNetCommandFactory.getLogin());

		gameCommandFactory = new NetGameCommandFactory();

		mainWindow = new MainWindow(gameCommandFactory, lobbyNetCommandFactory, game, this, config.getFrameBreak());
		lobbyStrategyFactory.setGameListUpdateListener(mainWindow.getGameListUpdateListener());
		lobbyStrategyFactory.setGameCreationListener(this);
		lobbyStrategyFactory.setLoginResponseResultListener(mainWindow);
		lobbyStrategyFactory.setJoinGameCommand(gameCommandFactory.getJoinGame());

		mainWindow.startUp(config);
	}
	
	public void execute() {
		FrameBreak frameBreak = new FrameBreak(config.getFrameBreak());
		Clock clock = new Clock();
		clock.reset();

		double x = 0;

		mainWindow.setVisible(true);
		while(mainWindow.isDisplayable()) {
			clock.update();
			frameBreak.breakFrame(clock.getElapsedMilliseconds());
			mainWindow.update();
			if(game.isActive()) {
				game.update(clock.getElapsedSeconds());
			}
		}
	}

	@Override
	public void gameSelected(int gamePort) {
		try {
			NetGameCommandFactory netGameCommandFactory = (NetGameCommandFactory)gameCommandFactory;

			GameStrategyFactory gameStrategyFactory = new GameStrategyFactory(
					mainWindow, 
					(IPlayerTeamJoinListener)PanelRegistry.getInstance().lookUpPanel(GameLobbyPanel.PANEL_NAME),
					game, 
					netGameCommandFactory);
			gameStrategyFactory.getGameStartedStrategy().addStartGameListener(mainWindow);
			gameStrategyFactory.getGameStartedStrategy().addStartGameListener(this);
			netGame.startup(config.getServerAddress(), gamePort, gameStrategyFactory);

			netGameCommandFactory.startup(netGame.getSender(), netGame.getServerConnection(), netGame.getQueryPort());

			netGame.setPreLoginResponseListener(netGameCommandFactory.getJoinGame());
			
			netGameCommandFactory.getJoinGame().setPlayerName(((SettingsPanel)PanelRegistry.getInstance().lookUpPanel(SettingsPanel.PANEL_NAME)).getPlayerName());
		}
		catch(StartupException e) {
			//TODO : make some listener for this that displays the error message
			logger.error(e);
		}
	}
	@Override
	public void createLocalGame(Settings settings) {	
		try {
			serverGame = GameCreator.createGame(settings, new SessionFactory().createSession());
		}
		catch (GameCreationException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		
		GameLobby gameLobby = null;
		try {
			gameLobby = new GameLobby(settings, GameCreator.createFields(settings));
			serverGameDemon = new GameDemon(serverGame, gameLobby);
		}
		catch (LobbyCreationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		catch (FieldCreationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		catch (SocketException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		new Thread(serverGameDemon).start();
			
		try {
			Sender sender = new Sender();
			ServerConnection serverConnection = new ServerConnection(InetAddress.getByName(null), serverGameDemon.getSocketPort());
			
			NetGameCommandFactory netGameCommandFactory = (NetGameCommandFactory)gameCommandFactory;

			GameStrategyFactory gameStrategyFactory = new GameStrategyFactory(
					mainWindow, 
					(IPlayerTeamJoinListener)PanelRegistry.getInstance().lookUpPanel(GameLobbyPanel.PANEL_NAME),
					this.game, 
					netGameCommandFactory);
			gameStrategyFactory.getGameStartedStrategy().addStartGameListener(mainWindow);
			gameStrategyFactory.getGameStartedStrategy().addStartGameListener(this);
			
			netGame.startup(InetAddress.getByName(null), serverGameDemon.getSocketPort(), gameStrategyFactory);

			netGameCommandFactory.startup(netGame.getSender(), netGame.getServerConnection(), netGame.getQueryPort());

			netGame.setPreLoginResponseListener(netGameCommandFactory.getJoinGame());
			
			netGameCommandFactory.getJoinGame().setPlayerName(((SettingsPanel)PanelRegistry.getInstance().lookUpPanel(SettingsPanel.PANEL_NAME)).getPlayerName());
			netGameCommandFactory.getJoinGame().join();
		}
		catch(StartupException e) {
			//TODO : make some listener for this that displays the error message
			logger.error(e);
		}
		catch (SocketException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	@Override
	public void joinGameDirectByIP(InetSocketAddress serverAddress){
	/*	NetGameCommandFactory netGameCommandFactory = (NetGameCommandFactory)gameCommandFactory;
		try {
			netGameCommandFactory.startup(new Sender(serverAddress.getPort()),
					   (new ServerConnection(serverAddress.getAddress(), serverAddress.getPort())).getServerConnection(),
					   serverAddress.getPort());
			netGameCommandFactory.getJoinGame().setPlayerName(
					((SettingsPanel)PanelRegistry.getInstance().lookUpPanel(SettingsPanel.PANEL_NAME)).getPlayerName());
			netGameCommandFactory.getJoinGame().join();
		}
		catch (SocketException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}*/
		try {
			NetGameCommandFactory netGameCommandFactory = (NetGameCommandFactory)gameCommandFactory;

			GameStrategyFactory gameStrategyFactory = new GameStrategyFactory(
					mainWindow, 
					(IPlayerTeamJoinListener)PanelRegistry.getInstance().lookUpPanel(GameLobbyPanel.PANEL_NAME),
					game, 
					netGameCommandFactory);
			gameStrategyFactory.getGameStartedStrategy().addStartGameListener(mainWindow);
			gameStrategyFactory.getGameStartedStrategy().addStartGameListener(this);
			netGame.startup(serverAddress.getAddress(), serverAddress.getPort(), gameStrategyFactory);

			netGameCommandFactory.startup(netGame.getSender(), netGame.getServerConnection(), netGame.getQueryPort());

			netGame.setPreLoginResponseListener(netGameCommandFactory.getJoinGame());
			
			netGameCommandFactory.getJoinGame().setPlayerName(((SettingsPanel)PanelRegistry.getInstance().lookUpPanel(SettingsPanel.PANEL_NAME)).getPlayerName());
			netGameCommandFactory.getJoinGame().join();
		}
		catch(StartupException e) {
			//TODO : make some listener for this that displays the error message
			logger.error(e);
		}
	}

	@Override
	public void hostPromoted(long hostIdentification) {
		((NetGameCommandFactory)gameCommandFactory).startupHostActions(hostIdentification);
	}

	@Override
	public void gameStarted() {
		game.setActive(true);
	}
	
	private void shutdown() {
		mainWindow.shutdown();
		lobby.shutdown();
		if (serverGameDemon != null){
			serverGameDemon.shutdown();
		}
	}
}
